package com.maowu.pooling;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

public class SafePool<C> extends SimplePool<C>
{
    private Object mutex = new Object();
    
    private HashMap<C, Long> lentMap = new HashMap<C, Long>();
    
    private long  repaymentCycle = 30*60*1000;//how long can the thread keep the connection. default is 30 minutes.

    private Timer timer;
    
    private boolean inited = false;
    
    /**
     * @param factory
     */
    public SafePool(ConnectionFactory factory)
    {
        super(factory);
    }

    public SafePool(ConnectionFactory factory, int maxConnection, long waitingTimeout, long connectionTimeout)
    {
        super(factory, maxConnection, waitingTimeout, connectionTimeout);
    }
    

    public SafePool(ConnectionFactory factory, int maxConnection, long waitingTimeout, long connectionTimeout, long repaymentCycle)
    {
        super(factory, maxConnection, waitingTimeout, connectionTimeout);
        this.repaymentCycle = repaymentCycle;
    }


    public C borrowConnection() throws FailedToConnectException, WaitingTimeoutException, PoolErrorException
    {
        checkInited();
        C c = super.borrowConnection();
        synchronized (mutex)
        {
            lentMap.put(c, Long.valueOf(System.currentTimeMillis()));
        }
        
        return c;
    }
    
    
    public void returnConnection(C conn) throws PoolErrorException
    {
        super.returnConnection(conn);
        synchronized (mutex)
        {
            lentMap.remove(conn);
        }
    }
    
    public void close()
    {
        clear();
        super.close();
    }
    
    private void checkInited()
    {
        if (!inited)
        {
            throw new IllegalStateException("Not yet inited, call init() before using it.");
        }
    }
    
    private void clear()
    {
        synchronized (mutex)
        {
            if (timer != null)
            {
                timer.cancel();
                timer = null;
            }
            
            lentMap.clear();
        }
    }
    
    public void init()
    {
        synchronized (mutex)
        {
            clear();
            
            timer = new Timer();
            long recheckCycle = repaymentCycle /10;//check every 1 tenth of repaymentCycle.
            if (recheckCycle <= 1)
            {
                recheckCycle = 1;
            }
            timer.schedule(new RecheckTask(), recheckCycle, recheckCycle);
        }
    }
    
    private final class RecheckTask extends TimerTask
    {
        private ArrayList<C> garbage = new ArrayList<C>();
        /* (non-Javadoc)
         * @see java.util.TimerTask#run()
         */
        @Override
        public void run()
        {
            try
            {
                long current = System.currentTimeMillis();
                synchronized (mutex)
                {
                    Iterator<Map.Entry<C, Long>> it = lentMap.entrySet().iterator();
                    while (it.hasNext())
                    {
                        Map.Entry<C, Long> entry = it.next();
                        if (current - entry.getValue().longValue() > repaymentCycle)
                        {
                            //the thread has kept this connection for too long, 
                            //destroy it.
                            garbage.add(entry.getKey());
                        }
                        
                    }
                    for (C conn : garbage)
                    {
                        lentMap.remove(conn);
                    }
                }
                
                for (C conn : garbage)
                {
                    connectionBroken(conn);
                }
            }
            catch (Throwable e)
            {
                e.printStackTrace();
            }
        }
        
    }

    public boolean isInited()
    {
        return inited;
    }



    public long getRepaymentCycle()
    {
        return repaymentCycle;
    }



    public void setRepaymentCycle(long repaymentCycle)
    {
        this.repaymentCycle = repaymentCycle;
    }
}
